#include "tcp_sender.hh"
#include "tcp_config.hh"

using namespace std;

RetransmissionTimer& RetransmissionTimer::active()noexcept{
  is_active_=true;
  return *this;
}
RetransmissionTimer& RetransmissionTimer::timeout()noexcept{
  RTO_<<=1;
  return *this;
}
RetransmissionTimer& RetransmissionTimer::reset()noexcept{
  time_passed_=0;
  return *this;
}
RetransmissionTimer& RetransmissionTimer::tick(uint64_t tick_ms)noexcept{
  time_passed_+=is_active_?tick_ms:0;
  return *this;
}


uint64_t TCPSender::sequence_numbers_in_flight() const
{
  // Your code here.
  return num_in_flight_;
}

uint64_t TCPSender::consecutive_retransmissions() const
{
  // Your code here.
  return retranmission_cnt_;
}

void TCPSender::push( const TransmitFunction& transmit )
{
  // Your code here.
  fin_flag_|=input_.reader().is_finished();//刚建立就关闭流
  if(send_fin_)
    return;
  //设置窗口长度
  const size_t win_size=wnd_==0?1:wnd_;
  //未发出fin且不超窗口时不断组装发送数据
  for(string payload{};num_in_flight_<win_size&&!send_fin_;payload.clear()){
    string_view byte_view=input_.reader().peek();
    //已发出发送请求但流为空且不需要发出fin 跳过本次报文发送
    if(send_syn_&&byte_view.empty()&&!fin_flag_)
      break;
    //从流中读取数据组装报文
    while(payload.size()+num_in_flight_+(!send_syn_)<win_size&&payload.size()<TCPConfig::MAX_PAYLOAD_SIZE){
      if(byte_view.empty()||fin_flag_)
        break;
      if(const uint64_t avail=min(TCPConfig::MAX_PAYLOAD_SIZE-payload.size(),win_size-(payload.size()+num_in_flight_+(!send_syn_)));
          byte_view.size()>avail)//截断
        byte_view.remove_suffix(byte_view.size()-avail);//移除后缀
      payload.append(byte_view);
      input_.reader().pop(byte_view.size());
      fin_flag_|=input_.reader().is_finished();
      byte_view=input_.reader().peek();
    }
    auto &msg=outstanding_byte_.emplace(message_(next_seqno_,move(payload),send_syn_?syn_flag_:true,fin_flag_));
    const size_t margin=send_syn_?syn_flag_:0;

    if(fin_flag_&&(msg.sequence_length()-margin)+num_in_flight_>win_size)
      msg.FIN=false;
    else if(fin_flag_)
      send_fin_=true;
    const size_t cor_len=msg.sequence_length()-margin;

    num_in_flight_+=cor_len;
    next_seqno_+=cor_len;
    send_syn_=true;
    transmit(msg);
    if(cor_len!=0)
      timer_.active();
  }

}

TCPSenderMessage TCPSender::make_empty_message() const
{
  // Your code here.
  return message_(next_seqno_,{},false);
}

void TCPSender::receive( const TCPReceiverMessage& msg )
{
  // Your code here.
  wnd_=msg.window_size;
  if(!msg.ackno.has_value()){
    if(msg.window_size==0)
      input_.set_error();
    return;
  }
  const uint64_t excepted_seqno=msg.ackno->unwrap(isn_,next_seqno_);
  if(excepted_seqno>next_seqno_)
    return;//不接受 因为未发送就被确认了
  bool is_ack=false;
  while(!outstanding_byte_.empty()){
    auto& buffered_msg = outstanding_byte_.front();
    if(const uint64_t final_seqno=ack_seqno_+buffered_msg.sequence_length()-buffered_msg.SYN;
         excepted_seqno<=ack_seqno_||excepted_seqno<final_seqno)
      break; 

    is_ack=true; // 表示有字节被确认
    num_in_flight_-=buffered_msg.sequence_length() - syn_flag_;
    ack_seqno_ += buffered_msg.sequence_length() - syn_flag_;
    // 最后检查 syn 是否被确认
    syn_flag_ = send_syn_ ? syn_flag_ : excepted_seqno <= next_seqno_;
    outstanding_byte_.pop();
  }

  if (is_ack){
    // 如果全部分组都被确认，那就停止计时器
    if(outstanding_byte_.empty())
      timer_=RetransmissionTimer(initial_RTO_ms_);
    else // 否则就只重启计时器
      timer_=move(RetransmissionTimer( initial_RTO_ms_ ).active());
    retranmission_cnt_ = 0; // 重置 RTO 值更换新对象
  }

}

void TCPSender::tick( uint64_t ms_since_last_tick, const TransmitFunction& transmit )
{
  // Your code here.
  if(timer_.tick(ms_since_last_tick).is_expired()){
    transmit(outstanding_byte_.front());//重发
    if(wnd_==0)
      timer_.reset();
    else 
      timer_.timeout().reset();
    ++retranmission_cnt_;
  }
}


TCPSenderMessage TCPSender::message_( uint64_t seqno, string payload, bool SYN, bool FIN ) const
{
  return {.seqno=Wrap32::wrap(seqno,isn_),
          .SYN = SYN,
          .payload = move(payload),
          .FIN = FIN,
          .RST = input_.reader().has_error()};
}
